// Server side C/C++ program to demonstrate Socket
// programming

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <functional>
#include <sys/poll.h>
#include "tcpSession.h"

tcpSock::tcpSock()
{
	m_bEventLoopStarted = false;
	m_darktsFlag = 1;
}

tcpSock::~tcpSock()
{
	;
}

tcpSock& tcpSock::Instance()
{
	static tcpSock _agent;
	return _agent;
}

void tcpSock::Init(const char *nIp,int nPort)
{
	memset(tcpBuf,'\0',sizeof(tcpBuf));
	memset(m_ip,'\0',sizeof(m_ip));
	memcpy(m_ip,nIp,strlen(nIp));
	m_port = nPort;

	m_bEventLoopStarted = true;
	int r = pthread_create (&m_tid, NULL, EventLoop, this);
    pthread_setname_np(m_tid,"tcpThread");
	if (r)
	{
		qlog_e ("pthread_create()");
		return;
	}
	qlog_i("Tcp Server initialized ...");

}

int tcpSock::CreateSocket()
{
    int status = 0;
    struct sockaddr_in serv_addr;
    if ((m_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		qlog_e("TCP Socket creation error");
        close(m_sock);
		m_sock = -1;
		return -1;
	}

    serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(m_port);

    if (inet_pton(AF_INET, m_ip, &serv_addr.sin_addr) <= 0) {
		qlog_e("TCP Invalid address/ Address not supported");
        close(m_sock);
		m_sock = -1;
		return -1;
	}

    if ((status = connect(m_sock, (struct sockaddr*)&serv_addr,sizeof(serv_addr))) < 0) {
		// qlog_e("TCP Connection Failed");
        close(m_sock);
		m_sock = -1;
		return -1;
	}
	return m_sock;
}


int tcpSock::Close()
{
	if (m_tid != 0)
	{
		m_bEventLoopStarted = false;
		pthread_join(m_tid, NULL);
	}
	if ( m_sock != -1)
	{
        shutdown(m_sock,SHUT_RDWR);
        close(m_sock);
        m_sock = -1;
	}
	return 0;
}


size_t tcpSock::Send( unsigned char* pBuf, size_t nLen ,size_t msg) const
{
	if (m_sock == -1)
		return 0;

	size_t nSendLen = 0;
	if(m_disconnect == 0){
		nSendLen = send(m_sock, pBuf, nLen, msg);
	}
	return nSendLen;

}


int tcpSock::Receive(char* pBuf, size_t nLen) const
{
	if(CmdFun != nullptr)
    {
        struct cmdDataType cmdStruct;
        memset(&cmdStruct,0,sizeof(cmdStruct));
        cmdStruct.dataLen = nLen;
        CmdFun(&cmdStruct,(uint8_t *)pBuf);
        return 0;
    }
    return -1;
}

void tcpSock::Register(CallbackCmdFun callbackFn)
{
    if(callbackFn != nullptr)
    {
        CmdFun = callbackFn;//receive
    }
}


void* tcpSock::EventLoop(void* lpParameter )
{
	tcpSock* pThis = (tcpSock*)lpParameter;
	pThis->ProcessEvent();
	return 0;
}

void tcpSock::ProcessEvent()
{
	struct pollfd pfd;
	int res = 0,err_num = 0,err_Socket = 0;
	int cnt, nbytes;
	while (m_bEventLoopStarted)//尝试重新创建与连接（当服务端断开再启动后，期间需要保持尝试连接）
	{
		err_Socket = CreateSocket();
		if (err_Socket > 0) {
			qlog_w("暗室上位机连接成功,connection tcp:%d",err_Socket);
			while(m_bEventLoopStarted) {
				pfd.fd = m_sock;
				pfd.events = POLLIN | POLLHUP | POLLRDNORM;
				pfd.revents = 0;
				m_disconnect = 0;
				res = poll(&pfd, 1, 60000);
				if(res >= 0){
					// if result > 0, this means that there is either data available on the
					// socket, or the socket has been closed
					cnt = recv(m_sock, tcpBuf, sizeof(tcpBuf)-1, MSG_PEEK | MSG_DONTWAIT);
					if( -1 == cnt) {
						err_num = errno;
						switch(err_num){
						case ECONNRESET:
						case EBADF:
						case EPIPE: 
						case ENOTSOCK:
							m_disconnect = 1;                            
							break;
						default:
							break;
						}
						if(m_disconnect){
							break;//退到createSocket
						}
						usleep(200000);
					} else if( 0 == cnt) {
						if(res > 0){
							// if recv returns zero, that means the connection has been closed:
							m_disconnect = 1;                            
							break;//退到createSocket
						}
						usleep(200000);
					} else if( cnt > 0 ) {
						while((nbytes = recv(m_sock, tcpBuf, sizeof(tcpBuf)-1, MSG_DONTWAIT)) > 0){
							Receive(tcpBuf,nbytes);
						}
					}	
				}
			}
		} else {
			sleep(5);
			continue;		
		}
		// shutdown(m_sock,SHUT_RDWR);
		qlog_w("暗室上位机断开连接,tcp socket shutdown");
		close(m_sock);
		m_sock = -1;
		sleep(20);
	}
}


int tcpSock::sendto(struct cmdDataType* cmd,uint8_t * cmdData)
{
	int sendLen = 0;
	int retLen = 0;
    if(cmd == NULL)
    {
        return -1;
    }
    if((cmd->dataLen > 0) && (cmdData == NULL))
    {
        return -2;
    }
    if(cmd->dataLen < 0)
    {
        return -3;
    }
	
	sendLen = cmd->dataLen+sizeof(struct cmdDataType);
	auto p_SendBuff = new uint8_t[sendLen];

	memcpy(p_SendBuff,cmd,sizeof(struct cmdDataType));
	memcpy((p_SendBuff+sizeof(struct cmdDataType)),cmdData,cmd->dataLen);
	retLen = Send(p_SendBuff,sendLen,0);
	delete[] p_SendBuff;
	p_SendBuff = nullptr;
	return retLen;
}

size_t tcpSock::sendto(uint8_t* data,size_t len)
{
	auto sendLen = len;
	if((sendLen < 0) || (data == nullptr))
    {
        return -1;
    }

	return Send(data,sendLen,0);
}